import datetime
import re
import time

import logzero
from PyQt5.QtCore import QDateTime, QRegExp, QTimer
from PyQt5.QtGui import QRegExpValidator
from PyQt5.QtWidgets import (
    QCheckBox,
    QComboBox,
    QDateTimeEdit,
    QDoubleSpinBox,
    QGridLayout,
    QGroupBox,
    QHBoxLayout,
    QLabel,
    QLineEdit,
    QMessageBox,
    QPushButton,
    QSpinBox,
    QVBoxLayout,
    QWidget,
)

import mengy_spac800
import mengy_spac800_bt
from helpers.mengy_bytes_helper import bytes2Int
from qt_custom_widget.qt_helper import addTooltipInSpinBox
from qt_custom_widget.qt_ScrollMessageBox import ScrollMessageBox

logger = logzero.setup_logger("tabs.2.sys")


class Tab2_Sys_Para(QWidget):
    def __init__(self, serRecvDir, sendQueuePack, logger=logger):
        super().__init__()
        self.initUI()
        self.serRecvDir = serRecvDir
        self.sendQueuePack = sendQueuePack
        self.logger = logger
        self.logger.info("this is tab2 sys para logger")
        self.refreshTime = time.time()
        self.setObjectName("tab2")
        layout = QGridLayout(self)
        layout.addWidget(self.datetimeParaGroupBox, 0, 0, 1, 2)  # 时间日期
        layout.addWidget(self.clearParaGroupBox, 1, 0, 1, 2)  # 清零操作
        layout.addWidget(self.remoteCtrlGroupBox, 2, 0, 1, 2)  # 遥控出口

        layout.addWidget(self.energyGroupBox, 0, 2, 3, 1)  # 电度设置

        layout.addWidget(self.commParaGroupBox, 3, 0, 4, 3)  # 通信参数

        layout.addWidget(self.sysParaGroupBox, 0, 3, 7, 2)  # 系统参数 变比
        layout.addWidget(self.HomeDispParaGroupBox, 0, 5, 7, 1)  # 显示设置

    def initUI(self):
        """初始化显示构件"""
        self.createDatetimeParaGroupBox()  # 时间日期
        self.createEnergyGroupBox()  # 电度设置
        self.createSysParaGroupBox()  # 系统参数
        self.createCommParaGroupBox()  # 通信参数
        self.createClearParaGroupBox()  # 清零电度
        self.createRemoteCtrlGroupBox()  # 开出遥控
        self.createHomeDispParaGroupBox()  # 首页轮显设置

    # 时间日期参数
    def createDatetimeParaGroupBox(self):
        """创建时间日期参数显示构件"""
        self.datetimeParaGroupBox = QGroupBox("日期时间")
        layout = QGridLayout()

        self.dateEdit = QDateTimeEdit(self.datetimeParaGroupBox)
        self.dateEdit.setDisplayFormat("yyyy-MM-dd HH:mm:ss")
        self.dateEdit.setDateTime(QDateTime.fromTime_t(0))
        layout.addWidget(self.dateEdit, 0, 0, 1, 3)

        self.datetimePara_ReadButton = QPushButton("读取")
        self.datetimePara_WrtieButton = QPushButton("写入")
        self.datetimePara_PCTimeButton = QPushButton("同步电脑时间")
        layout.addWidget(self.datetimePara_ReadButton, 1, 0)
        layout.addWidget(self.datetimePara_WrtieButton, 1, 1)
        layout.addWidget(self.datetimePara_PCTimeButton, 1, 2)
        self.datetimeParaGroupBox.setLayout(layout)

        self.datetimePara_ReadButton.clicked.connect(self.dealDatetimeButtonClicked)
        self.datetimePara_WrtieButton.clicked.connect(self.dealDatetimeButtonClicked)
        self.datetimePara_PCTimeButton.clicked.connect(self.dealDatetimeButtonClicked)

    def _sendWriteDateTimePack(self, totalSecond):
        wd = datetime.datetime.fromtimestamp(totalSecond)
        self.dateEdit.setStyleSheet("color:#ff0000;")
        self.logger.info("write datetime is {}".format(wd))
        data = (wd.year - 2000, wd.month * 256 + wd.day, wd.hour * 256 + wd.minute, wd.second * 1000)
        self.sendQueuePack(funCode=0x10, start=0x0000, data=data)
        QTimer.singleShot(1500, self.datetimeParaGroupBoxRead)

    def dealDatetimeButtonClicked(self):
        """时间日期参数按钮处理"""
        sender = self.sender()
        # self.logger.info(sender is self.datetimePara_ReadButton)
        if sender is self.datetimePara_ReadButton:  # 读取日期时间
            self.datetimeParaGroupBoxRead()
        elif sender is self.datetimePara_PCTimeButton:  # 写入电脑日期时间
            totalSecond = QDateTime.currentDateTime().toTime_t()
            self._sendWriteDateTimePack(totalSecond)
        elif sender is self.datetimePara_WrtieButton:  # 写入日期时间
            if self.dateEdit.date().year() >= 2000:
                totalSecond = self.dateEdit.dateTime().toTime_t()
                self._sendWriteDateTimePack(totalSecond)

    def datetimeParaGroupBoxRead(self):
        """显示时间日期"""
        if any((key not in self.serRecvDir.keys() for key in range(0x0000, 0x0003 + 1))):
            return
        year = 2000 + self.serRecvDir.get(0x0000)[1]
        month, day = self.serRecvDir.get(0x0001)
        hour, minute = self.serRecvDir.get(0x0002)
        second = bytes2Int(self.serRecvDir.get(0x0003)) // 1000
        microsecond = bytes2Int(self.serRecvDir.get(0x0003)) % 1000 * 1000
        try:
            readDatetime = datetime.datetime(year, month, day, hour, minute, second, microsecond)
            delatUTC = datetime.datetime.now() - datetime.datetime.utcnow()
            timeCnt = int((readDatetime - datetime.datetime(1970, 1, 1) - delatUTC).total_seconds())
            self.dateEdit.setStyleSheet("")
            self.dateEdit.setDateTime(QDateTime.fromTime_t(timeCnt))
        except Exception:
            self.logger.exception("datetime convert failed")

    # 电度设置
    def createEnergyGroupBox(self):
        """创建电度设置显示构件"""
        self.energyOverRangeFlag = False
        self.energyGroupBox = QGroupBox("电度数据")
        layout = QGridLayout(self.energyGroupBox)

        self.energeyPP = QDoubleSpinBox()
        self.energeyPP.setDecimals(1)
        self.energeyPP.setSuffix("kWh")
        addTooltipInSpinBox(self.energeyPP, (0.0, 429496729.5))
        layout.addWidget(QLabel("正向有功"), 0, 0, 1, 1)
        layout.addWidget(self.energeyPP, 0, 1, 1, 2)

        self.energeyPN = QDoubleSpinBox()
        self.energeyPN.setDecimals(1)
        self.energeyPN.setSuffix("kWh")
        addTooltipInSpinBox(self.energeyPN, (0.0, 429496729.5))
        layout.addWidget(QLabel("负向有功"), 1, 0, 1, 1)
        layout.addWidget(self.energeyPN, 1, 1, 1, 2)

        self.energeyNP = QDoubleSpinBox()
        self.energeyNP.setDecimals(1)
        self.energeyNP.setSuffix("kVar")
        addTooltipInSpinBox(self.energeyNP, (0.0, 429496729.5))
        layout.addWidget(QLabel("正向无功"), 2, 0, 1, 1)
        layout.addWidget(self.energeyNP, 2, 1, 1, 2)

        self.energeyNN = QDoubleSpinBox()
        self.energeyNN.setDecimals(1)
        self.energeyNN.setSuffix("kVar")
        addTooltipInSpinBox(self.energeyNN, (0.0, 429496729.5))
        layout.addWidget(QLabel("负向无功"), 3, 0, 1, 1)
        layout.addWidget(self.energeyNN, 3, 1, 1, 2)

        self.readEnergyButton = QPushButton("读取")
        layout.addWidget(self.readEnergyButton, 4, 0, 1, 1)

        self.clearEnergyButton = QPushButton("电度清零")
        layout.addWidget(self.clearEnergyButton, 4, 1, 1, 1)

        self.readEnergyButton.clicked.connect(self.dealEnergyButtonClicked)
        self.clearEnergyButton.clicked.connect(self.dealEnergyButtonClicked)

    def energeyGroupBoxRead(self):
        """显示电度"""
        if any((key not in self.serRecvDir.keys() for key in range(0x0040, 0x004F + 1))):
            return
        overRangeList = []
        for i, qdb in enumerate((self.energeyPP, self.energeyPN)):
            data = bytes2Int(self.serRecvDir.get(0x0040 + 2 * i) + self.serRecvDir.get(0x0041 + 2 * i)) / 10.0
            qdb.setValue(data)
            maximum = qdb.maximum()
            minimum = qdb.minimum()
            if data > maximum or data < minimum:
                overRangeList.append("{} 读取值 {} 范围 {} ~ {}".format(("正向有功", "负向有功")[i], data, minimum, maximum))
                qdb.setStyleSheet("color:#ff0000;")
            else:
                qdb.setStyleSheet("")
        for i, qdb in enumerate((self.energeyNP, self.energeyNN)):
            data = bytes2Int(self.serRecvDir.get(0x0048 + 2 * i) + self.serRecvDir.get(0x0049 + 2 * i)) / 10.0
            qdb.setValue(data)
            maximum = qdb.maximum()
            minimum = qdb.minimum()
            if data > maximum or data < minimum:
                overRangeList.append("{} 读取值 {} 范围 {} ~ {}".format(("正向无功", "负向无功")[i], data, minimum, maximum))
                qdb.setStyleSheet("color:#ff0000;")
            else:
                qdb.setStyleSheet("")

        if len(overRangeList) > 0 and not self.energyOverRangeFlag:
            self.energyOverRangeFlag = True
            messageBox = ScrollMessageBox(msgList=overRangeList, width=330, parent=self)
            messageBox.setWindowTitle("实时数据-电度参数 超范围")
            messageBox.exec_()
            self.energyOverRangeFlag = False

    def energeyGroupBoxWrite(self):
        """写入电度"""
        data = []
        for qdb in (self.energeyPP, self.energeyPN):
            energy = int(round(qdb.value() * (10 ** qdb.decimals())))
            data.append(energy // (2 ** 16))
            data.append(energy % (2 ** 16))
            qdb.setStyleSheet("color:#ff0000;")
        # self.logger.info(f'P energy is {data}')
        self.sendQueuePack(funCode=0x10, start=0x0040, data=data)

        data = []
        for qdb in (self.energeyNP, self.energeyNN):
            energy = int(round(qdb.value() * (10 ** qdb.decimals())))
            data.append(energy // (2 ** 16))
            data.append(energy % (2 ** 16))
            qdb.setStyleSheet("color:#ff0000;")
        # self.logger.info(f'N energy is {data}')
        self.sendQueuePack(funCode=0x10, start=0x0048, data=data)

    def dealEnergyButtonClicked(self):
        """处理电度设置按钮"""
        sender = self.sender()
        # self.logger.info(sender is self.readEnergyButton)
        if sender is self.readEnergyButton:  # 读取电度
            self.energeyGroupBoxRead()
        elif sender is self.clearEnergyButton:  # 清零电度
            self.logger.info("start clear energey")
            self.sendQueuePack(funCode=0x06, start=0x0B21, value=0xFF00)
            QTimer.singleShot(1500, self.energeyGroupBoxRead)

    # 系统参数
    def createSysParaGroupBox(self):
        """创建系统参数显示构件"""
        self.sysParaOverRangeFlagPrt = False
        self.sysParaOverRangeFlagMea = False
        self.sysParaGroupBox = QGroupBox("系统参数")
        layout = QGridLayout(self.sysParaGroupBox)

        ptrGroupBox = QGroupBox("保护参数")
        ptrLayout = QGridLayout(ptrGroupBox)
        self.meaGroupBox = QGroupBox("测量参数")
        meaLayout = QGridLayout(self.meaGroupBox)

        self.sysParaPrtPT = QDoubleSpinBox()
        self.sysParaPrtPT.setDecimals(1)
        self.sysParaPrtPT.setSingleStep(0.1)
        addTooltipInSpinBox(self.sysParaPrtPT, (1.0, 1000.0))
        ptrLayout.addWidget(QLabel("保护PT变比"), 0, 0)
        ptrLayout.addWidget(self.sysParaPrtPT, 0, 1)

        self.sysParaPrtCT = QSpinBox()
        addTooltipInSpinBox(self.sysParaPrtCT, (1, 10000))
        ptrLayout.addWidget(QLabel("保护CT变比"), 1, 0)
        ptrLayout.addWidget(self.sysParaPrtCT, 1, 1)

        self.sysParaPrtIX = QSpinBox()
        addTooltipInSpinBox(self.sysParaPrtIX, (1, 10000))
        ptrLayout.addWidget(QLabel("保护IX变比"), 2, 0)
        ptrLayout.addWidget(self.sysParaPrtIX, 2, 1)

        self.sysParaPrtUX = QDoubleSpinBox()
        self.sysParaPrtUX.setDecimals(1)
        self.sysParaPrtUX.setSingleStep(0.1)
        addTooltipInSpinBox(self.sysParaPrtUX, (1.0, 1000.0))
        ptrLayout.addWidget(QLabel("保护UX变比"), 3, 0)
        ptrLayout.addWidget(self.sysParaPrtUX, 3, 1)

        self.sysParaPrtI0 = QComboBox()
        self.sysParaPrtI0.addItems(mengy_spac800.Mea_3I0_C_NAMES)
        ptrLayout.addWidget(QLabel("零序电流选择"), 4, 0)
        ptrLayout.addWidget(self.sysParaPrtI0, 4, 1)

        self.sysParaPrt_ReadButton = QPushButton("读取")
        self.sysParaPrt_WrtieButton = QPushButton("写入")
        ptrLayout.addWidget(self.sysParaPrt_ReadButton, 5, 0)
        ptrLayout.addWidget(self.sysParaPrt_WrtieButton, 5, 1)

        self.sysParaMeaPT = QDoubleSpinBox()
        self.sysParaMeaPT.setDecimals(1)
        self.sysParaMeaPT.setSingleStep(0.1)
        addTooltipInSpinBox(self.sysParaMeaPT, (0.1, 100.0))
        meaLayout.addWidget(QLabel("测量PT变比"), 1, 0)
        meaLayout.addWidget(self.sysParaMeaPT, 1, 1)

        self.sysParaMeaCT = QSpinBox()
        addTooltipInSpinBox(self.sysParaMeaCT, (1, 1000))
        meaLayout.addWidget(QLabel("测量CT变比"), 2, 0)
        meaLayout.addWidget(self.sysParaMeaCT, 2, 1)

        self.sysParaMeaCo = QComboBox()
        self.sysParaMeaCo.addItems(mengy_spac800.Mea_LN_NAMES)
        meaLayout.addWidget(QLabel("测量接线方式"), 3, 0)
        meaLayout.addWidget(self.sysParaMeaCo, 3, 1)

        self.sysParaMea_ReadButton = QPushButton("读取")
        self.sysParaMea_WrtieButton = QPushButton("写入")
        meaLayout.addWidget(self.sysParaMea_ReadButton, 4, 0)
        meaLayout.addWidget(self.sysParaMea_WrtieButton, 4, 1)

        self.sysParaPrt_ReadButton.clicked.connect(self.dealSysParaButtonClicked)
        self.sysParaPrt_WrtieButton.clicked.connect(self.dealSysParaButtonClicked)
        self.sysParaMea_ReadButton.clicked.connect(self.dealSysParaButtonClicked)
        self.sysParaMea_WrtieButton.clicked.connect(self.dealSysParaButtonClicked)

        layout.addWidget(ptrGroupBox, 0, 0)
        layout.addWidget(self.meaGroupBox, 1, 0)

    def sysParaGroupBoxReadByType(self, t):
        """系统参数读取"""
        if any((key not in self.serRecvDir.keys() for key in range(0x0200, 0x0207 + 1))):
            return
        overRangeList = []
        if t == 0:
            data = [bytes2Int(self.serRecvDir.get(0x0200 + i, b"\x00\x00")) for i in range(5)]
            self.logger.info("sys para prt is {}".format(data))
            self.sysParaPrtPT.setValue(data[0] * 0.1)
            maximum = self.sysParaPrtPT.maximum()
            minimum = self.sysParaPrtPT.minimum()
            if data[0] * 0.1 > maximum or data[0] * 0.1 < minimum:
                overRangeList.append("{} 读取值 {} 范围 {} ~ {}".format("保护CT变比定值", data[0] * 0.1, minimum, maximum))
                self.sysParaPrtPT.setStyleSheet("color:#ff0000;")
            else:
                self.sysParaPrtPT.setStyleSheet("")

            self.sysParaPrtCT.setValue(data[1] * 1)
            maximum = self.sysParaPrtCT.maximum()
            minimum = self.sysParaPrtCT.minimum()
            if data[1] * 1 > maximum or data[1] * 1 < minimum:
                overRangeList.append("{} 读取值 {} 范围 {} ~ {}".format("保护PT变比定值", data[1] * 1, minimum, maximum))
                self.sysParaPrtCT.setStyleSheet("color:#ff0000;")
            else:
                self.sysParaPrtCT.setStyleSheet("")

            self.sysParaPrtIX.setValue(data[2] * 1)
            maximum = self.sysParaPrtIX.maximum()
            minimum = self.sysParaPrtIX.minimum()
            if data[2] * 1 > maximum or data[2] * 1 < minimum:
                overRangeList.append("{} 读取值 {} 范围 {} ~ {}".format("保护IX变比定值", data[2] * 1, minimum, maximum))
                self.sysParaPrtIX.setStyleSheet("color:#ff0000;")
            else:
                self.sysParaPrtIX.setStyleSheet("")

            self.sysParaPrtUX.setValue(data[3] * 0.1)
            maximum = self.sysParaPrtUX.maximum()
            minimum = self.sysParaPrtUX.minimum()
            if data[3] * 0.1 > maximum or data[3] * 0.1 < minimum:
                overRangeList.append("{} 读取值 {} 范围 {} ~ {}".format("保护UX变比定值", data[3] * 0.1, minimum, maximum))
                self.sysParaPrtUX.setStyleSheet("color:#ff0000;")
            else:
                self.sysParaPrtUX.setStyleSheet("")

            self.sysParaPrtI0.setCurrentIndex(data[4])
            if data[4] >= self.sysParaPrtI0.count():
                overRangeList.append("{} 读取值越限 {}".format("零序电流选择定值", data[4]))
                self.sysParaPrtI0.setStyleSheet("color:#ff0000;")
            else:
                self.sysParaPrtI0.setStyleSheet("")

            if len(overRangeList) > 0 and not self.sysParaOverRangeFlagPrt:
                self.sysParaOverRangeFlagPrt = True
                messageBox = ScrollMessageBox(msgList=overRangeList, width=430, parent=self)
                messageBox.setWindowTitle("系统参数-保护参数 超范围")
                messageBox.exec_()
                self.sysParaOverRangeFlagPrt = False
        else:
            data = [bytes2Int(self.serRecvDir.get(0x0205 + i, b"\x00\x00")) for i in range(3)]
            self.logger.info("sys para mea is {}".format(data))
            self.sysParaMeaPT.setValue(data[0] * 0.1)
            maximum = self.sysParaMeaPT.maximum()
            minimum = self.sysParaMeaPT.minimum()
            if data[0] * 0.1 > maximum or data[0] * 0.1 < minimum:
                overRangeList.append("{} 读取值 {} 范围 {} ~ {}".format("测量CT变比定值", data[0] * 0.1, minimum, maximum))
                self.sysParaMeaPT.setStyleSheet("color:#ff0000;")
            else:
                self.sysParaMeaPT.setStyleSheet("")

            self.sysParaMeaCT.setValue(data[1] * 1)
            maximum = self.sysParaMeaCT.maximum()
            minimum = self.sysParaMeaCT.minimum()
            if data[1] * 1 > maximum or data[1] * 1 < minimum:
                overRangeList.append("{} 读取值 {} 范围 {} ~ {}".format("测量PT变比定值", data[1] * 1, minimum, maximum))
                self.sysParaMeaCT.setStyleSheet("color:#ff0000;")
            else:
                self.sysParaMeaCT.setStyleSheet("")

            self.sysParaMeaCo.setCurrentIndex(data[2])
            if data[2] >= self.sysParaMeaCo.count():
                overRangeList.append("{} 读取值越限 {}".format("测量接线方式选择定值", data[2]))
                self.sysParaMeaCo.setStyleSheet("color:#ff0000;")
            else:
                self.sysParaMeaCo.setStyleSheet("")

            if len(overRangeList) > 0 and not self.sysParaOverRangeFlagMea:
                self.sysParaOverRangeFlagMea = True
                messageBox = ScrollMessageBox(msgList=overRangeList, width=330, parent=self)
                messageBox.setWindowTitle("系统参数-测量参数 超范围")
                messageBox.exec_()
                self.sysParaOverRangeFlagMea = False

    def dealSysParaButtonClicked(self):
        """系统参数按键处理"""
        sender = self.sender()
        # self.logger.info(sender is self.sysParaPrt_ReadButton)
        if sender is self.sysParaPrt_ReadButton:  # 读取保护变比
            self.sysParaGroupBoxReadByType(0)
        elif sender is self.sysParaPrt_WrtieButton:  # 写入保护变比
            data = []
            data.append(int(round(self.sysParaPrtPT.value() * (10 ** self.sysParaPrtPT.decimals()))))
            data.append(self.sysParaPrtCT.value())
            data.append(self.sysParaPrtIX.value())
            data.append(int(round(self.sysParaPrtUX.value() * (10 ** self.sysParaPrtUX.decimals()))))
            data.append(self.sysParaPrtI0.currentIndex())
            for w in (self.sysParaPrtPT, self.sysParaPrtCT, self.sysParaPrtIX, self.sysParaPrtUX, self.sysParaPrtI0):
                w.setStyleSheet("color:#ff0000;")
            self.logger.info("set sys ptr {}".format(data))
            self.sendQueuePack(funCode=0x10, start=0x0200, data=data)
            QTimer.singleShot(1500, lambda: self.sysParaGroupBoxReadByType(0))
        elif sender is self.sysParaMea_ReadButton:  # 读取测量变比
            self.sysParaGroupBoxReadByType(1)
        elif sender is self.sysParaMea_WrtieButton:  # 写入测量变比
            data = []
            data.append(int(round(self.sysParaMeaPT.value() * (10 ** self.sysParaMeaPT.decimals()))))
            data.append(self.sysParaMeaCT.value())
            data.append(self.sysParaMeaCo.currentIndex())
            for w in (self.sysParaMeaPT, self.sysParaMeaCT, self.sysParaMeaCo):
                w.setStyleSheet("color:#ff0000;")
            self.logger.info("set sys mea {}".format(data))
            self.sendQueuePack(funCode=0x10, start=0x0205, data=data)
            QTimer.singleShot(1500, lambda: self.sysParaGroupBoxReadByType(1))

    def sysParaGroupBoxRead(self):
        """系统参数读取"""
        self.sysParaGroupBoxReadByType(1)
        self.sysParaGroupBoxReadByType(0)

    def createClearParaGroupBox(self):
        """清零操作界面配置"""
        self.clearParaGroupBox = QGroupBox("清零操作")
        layout = QGridLayout(self.clearParaGroupBox)

        self.clearPrtEventButton = QPushButton("保护事件清零")
        self.clearPrtEventButton.clicked.connect(self.dealClearParaButton)
        layout.addWidget(self.clearPrtEventButton, 0, 0)

        self.clearRmtEventButton = QPushButton("遥信事件清零")
        self.clearRmtEventButton.clicked.connect(self.dealClearParaButton)
        layout.addWidget(self.clearRmtEventButton, 0, 1)

        self.clearRecEventButton = QPushButton("故障录波清零")
        self.clearRecEventButton.clicked.connect(self.dealClearParaButton)
        layout.addWidget(self.clearRecEventButton, 1, 0)

        self.clear2ResetPrtButton = QPushButton("信号复归")
        self.clear2ResetPrtButton.clicked.connect(self.dealClearParaButton)
        layout.addWidget(self.clear2ResetPrtButton, 1, 1)

    def dealClearParaButton(self):
        """清零事件及复归按键处理"""
        sender = self.sender()
        choice = QMessageBox.question(self, "警告", "执行->{}？".format(sender.text()), QMessageBox.Yes | QMessageBox.No)
        if choice == QMessageBox.Yes:
            if sender is self.clearPrtEventButton:  # 清除历史保护事件
                self.sendQueuePack(funCode=0x06, start=0x0B22, value=0xFF00)
            elif sender is self.clearRmtEventButton:  # 清除历史遥信事件
                self.sendQueuePack(funCode=0x06, start=0x0B23, value=0xFF00)
            elif sender is self.clearRecEventButton:  # 清除故障录波
                self.sendQueuePack(funCode=0x06, start=0x0B24, value=0xFF00)
            elif sender is self.clear2ResetPrtButton:  # 信号复归
                self.sendQueuePack(funCode=0x06, start=0x0B20, value=0xFF00)

    def createRemoteCtrlGroupBox(self):
        """遥控出口"""
        self.remoteCtrlGroupBox = QGroupBox("遥控出口")
        layout = QGridLayout(self.remoteCtrlGroupBox)

        self.remoteCtrlOffButton = QPushButton("遥控跳闸")
        layout.addWidget(self.remoteCtrlOffButton, 0, 0)
        self.remoteCtrlOffButton.clicked.connect(self.dealRemoteCtrlButton)

        self.remoteCtrlOnButton = QPushButton("遥控合闸")
        layout.addWidget(self.remoteCtrlOnButton, 0, 1)
        self.remoteCtrlOnButton.clicked.connect(self.dealRemoteCtrlButton)

    def dealRemoteCtrlButton(self):
        """遥控分合闸按键处理"""
        sender = self.sender()
        choice = QMessageBox.question(self, "警告", "执行->{}？".format(sender.text()), QMessageBox.Yes | QMessageBox.No)
        if choice == QMessageBox.Yes:
            if sender is self.remoteCtrlOffButton:  # 遥控跳闸
                self.sendQueuePack(funCode=0x06, start=0x0B00, value=0xFF00)
            elif sender is self.remoteCtrlOnButton:  # 遥控合闸
                self.sendQueuePack(funCode=0x06, start=0x0B01, value=0xFF00)

    def createCommParaGroupBox(self):
        """创建通讯参数显示构件"""
        POTOL_ITEMS = ("modbus", "103", "内部调试")
        self.commParaSerOverRangeFlags = [False, False]
        self.commParaNetNetOverRangeFlag = False
        self.commParaGroupBox = QGroupBox("通信参数")
        layout = QGridLayout(self.commParaGroupBox)

        g1_box = QGroupBox("串口参数")
        g3_box = QGroupBox("网口参数")

        g1_layout = QGridLayout(g1_box)
        g3_layout = QGridLayout(g3_box)

        self.commParaSerAddr1 = QSpinBox()
        addTooltipInSpinBox(self.commParaSerAddr1, (1, 250))
        g1_layout.addWidget(QLabel("串口1地址"), 0, 0)
        g1_layout.addWidget(self.commParaSerAddr1, 0, 1)

        self.commParaSerBaud1 = QComboBox()
        self.commParaSerBaud1.addItems(mengy_spac800.COMM_BAUD_LIST)
        g1_layout.addWidget(QLabel("串口1波特率"), 1, 0)
        g1_layout.addWidget(self.commParaSerBaud1, 1, 1)

        self.commParaSerPot1 = QComboBox()
        self.commParaSerPot1.addItems(POTOL_ITEMS)
        g1_layout.addWidget(QLabel("串口1规约"), 2, 0)
        g1_layout.addWidget(self.commParaSerPot1, 2, 1)

        self.commParaSerAddr2 = QSpinBox()
        addTooltipInSpinBox(self.commParaSerAddr2, (1, 250))
        g1_layout.addWidget(QLabel("串口2地址"), 3, 0)
        g1_layout.addWidget(self.commParaSerAddr2, 3, 1)

        self.commParaSerBaud2 = QComboBox()
        self.commParaSerBaud2.addItems(mengy_spac800.COMM_BAUD_LIST)
        g1_layout.addWidget(QLabel("串口2波特率"), 4, 0)
        g1_layout.addWidget(self.commParaSerBaud2, 4, 1)

        self.commParaSerPot2 = QComboBox()
        self.commParaSerPot2.addItems(POTOL_ITEMS[:2])
        g1_layout.addWidget(QLabel("串口2规约"), 5, 0)
        g1_layout.addWidget(self.commParaSerPot2, 5, 1)

        button_layout = QHBoxLayout()
        self.commParaSer1_ReadButton = QPushButton("读取")
        self.commParaSer1_WrtieButton = QPushButton("写入")

        button_layout.addWidget(self.commParaSer1_ReadButton)
        button_layout.addWidget(self.commParaSer1_WrtieButton)
        g1_layout.addLayout(button_layout, 6, 0, 1, 2)

        ipRange = "(?:[0-1]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])"  # Part of the regular expression
        # Regulare expression
        ipRegex = QRegExp("^" + ipRange + "\\." + ipRange + "\\." + ipRange + "\\." + ipRange + "$")
        validator = QRegExpValidator(ipRegex)

        self.commParaIP1 = QLineEdit("192.168.1.2")
        self.commParaIP1.setValidator(validator)
        g3_layout.addWidget(QLabel("IP 地址"), 0, 0, 1, 1)
        g3_layout.addWidget(self.commParaIP1, 0, 1, 1, 3)

        self.commParaMask1 = QLineEdit("255.255.255.0")
        self.commParaMask1.setValidator(validator)
        g3_layout.addWidget(QLabel("子网掩码"), 1, 0, 1, 1)
        g3_layout.addWidget(self.commParaMask1, 1, 1, 1, 3)

        self.commParaGate1 = QLineEdit("192.168.1.1")
        self.commParaGate1.setValidator(validator)
        g3_layout.addWidget(QLabel("网关"), 2, 0, 1, 1)
        g3_layout.addWidget(self.commParaGate1, 2, 1, 1, 3)

        self.commParaNetPot1 = QComboBox()
        self.commParaNetPot1.addItems(POTOL_ITEMS[:2])
        g3_layout.addWidget(QLabel("网口规约"), 3, 0, 1, 1)
        g3_layout.addWidget(self.commParaNetPot1, 3, 1, 1, 3)

        self.commParaNet1_ReadButton = QPushButton("读取")
        self.commParaNet1_WrtieButton = QPushButton("写入")
        g3_layout.addWidget(self.commParaNet1_ReadButton, 4, 1, 1, 1)
        g3_layout.addWidget(self.commParaNet1_WrtieButton, 4, 3, 1, 1)
        g3_layout.setRowStretch(4, 1)

        layout.addWidget(g1_box, 0, 0)
        layout.addWidget(g3_box, 0, 1)

        self.commParaSer1_ReadButton.clicked.connect(self.dealCommParaButtonClicked)
        self.commParaSer1_WrtieButton.clicked.connect(self.dealCommParaButtonClicked)
        self.commParaNet1_ReadButton.clicked.connect(self.dealCommParaButtonClicked)
        self.commParaNet1_WrtieButton.clicked.connect(self.dealCommParaButtonClicked)

    def dealCommParaSerRead(self, serIndex):
        """读取通信参数-串口参数"""
        if any((key not in self.serRecvDir.keys() for key in range(0x0600, 0x0605 + 1))):
            return
        overRangeList = []
        if serIndex == 0:
            values = [bytes2Int(self.serRecvDir.get(0x0600 + i)) for i in range(3)]
            wl = (self.commParaSerAddr1, self.commParaSerBaud1, self.commParaSerPot1)
        if serIndex == 1:
            values = [bytes2Int(self.serRecvDir.get(0x0603 + i)) for i in range(3)]
            wl = (self.commParaSerAddr2, self.commParaSerBaud2, self.commParaSerPot2)
        wl[0].setValue(values[0])
        maximum = wl[0].maximum()
        minimum = wl[0].minimum()
        if values[0] > maximum or values[0] < minimum:
            overRangeList.append("串口{}地址定值 读取值{} 范围 {} ~ {}".format(serIndex + 1, values[0], minimum, maximum))
            wl[0].setStyleSheet("color:#ff0000;")
        else:
            wl[0].setStyleSheet("")

        wl[1].setCurrentIndex(values[1] - 1)
        if values[1] - 1 >= wl[1].count():
            overRangeList.append("串口{}波特率定值 读取值越限 {}".format(serIndex + 1, values[1]))
            wl[1].setStyleSheet("color:#ff0000;")
        else:
            wl[1].setStyleSheet("")

        wl[2].setCurrentIndex(values[2])
        if values[2] >= wl[1].count():
            overRangeList.append("串口{}规约定值 读取值越限 {}".format(serIndex + 1, values[2]))
            wl[2].setStyleSheet("color:#ff0000;")
        else:
            wl[2].setStyleSheet("")

        if len(overRangeList) > 0 and not self.commParaSerOverRangeFlags[serIndex]:
            self.commParaSerOverRangeFlags[serIndex] = True
            messageBox = ScrollMessageBox(msgList=overRangeList, width=330, parent=self)
            messageBox.setWindowTitle("通讯参数-串口{}参数 超范围".format(serIndex + 1))
            messageBox.exec_()
            self.commParaSerOverRangeFlags[serIndex] = False

    def dealCommParaSerWrite(self, serIndex):
        """写入通信参数-串口参数"""
        data = []
        if serIndex == 0:
            start = 0x0600
            wl = (self.commParaSerAddr1, self.commParaSerBaud1, self.commParaSerPot1)
        if serIndex == 1:
            start = 0x0603
            wl = (self.commParaSerAddr2, self.commParaSerBaud2, self.commParaSerPot2)
        for w in wl:
            w.setStyleSheet("color:#ff0000;")
        data.append(wl[0].value())
        data.append(wl[1].currentIndex() + 1)
        data.append(wl[2].currentIndex())
        self.sendQueuePack(funCode=0x10, start=start, data=data)

    def dealCommParaNetRead(self):
        """读取通信参数-网口参数"""
        if any((key not in self.serRecvDir.keys() for key in range(0x0606, 0x0612 + 1))):
            return
        overRangeList = []
        values = [bytes2Int(self.serRecvDir.get(0x0606 + i)) for i in range(13)]
        wl = (self.commParaIP1, self.commParaMask1, self.commParaGate1, self.commParaNetPot1)
        for i in range(3):
            data = [values[4 * i + j] for j in range(4)]
            ipStr = ".".join(("{:3d}".format(d) for d in data))
            itemName = ("IP地址", "子网掩码", "网关")[i]
            if all((d <= 255 for d in data)):
                wl[i].setText(ipStr)
                wl[i].setStyleSheet("")
            else:
                wl[i].setText("0.0.0.0")
                wl[i].setStyleSheet("color:#ff0000;")
                overRangeList.append("网口 {}定值 读取值{} 范围 0.0.0.0 ~ 255.255.255.255".format(itemName, ipStr))
        wl[3].setCurrentIndex(values[12])
        if values[12] >= wl[3].count():
            overRangeList.append("网口 规约定值 读取值越限 {}".format(values[12]))
            wl[3].setStyleSheet("color:#ff0000;")
        else:
            wl[3].setStyleSheet("")
        if len(overRangeList) > 0 and not self.commParaNetNetOverRangeFlag:
            self.commParaNetNetOverRangeFlag = True
            messageBox = ScrollMessageBox(msgList=overRangeList, width=500, parent=self)
            messageBox.setWindowTitle("通讯参数-网口参数 超范围")
            messageBox.exec_()
            self.commParaNetNetOverRangeFlag = False

    def dealCommParaNetWrite(self):
        """写入通信参数-网口参数"""
        data = []
        start = 0x0606
        wl = (self.commParaIP1, self.commParaMask1, self.commParaGate1, self.commParaNetPot1)
        for et in wl[:3]:
            for num in re.compile(r"\d+").findall(et.text()):
                data.append(int(num))
        data.append(wl[3].currentIndex())
        for w in wl:
            w.setStyleSheet("color:#ff0000;")
        self.sendQueuePack(funCode=0x10, start=start, data=data)

    def dealCommParaButtonClicked(self):
        """通信参数 按钮处理"""
        sender = self.sender()
        # self.logger.info("comm para button clicked {}".format(sender))
        if sender is self.commParaSer1_ReadButton:  # 串口1 参数 读取
            self.dealCommParaSerRead(0)
            self.dealCommParaSerRead(1)
        elif sender is self.commParaSer1_WrtieButton:  # 串口1 参数 写入
            self.dealCommParaSerWrite(0)
            QTimer.singleShot(3000, lambda: self.dealCommParaSerRead(0))
            self.dealCommParaSerWrite(1)
            QTimer.singleShot(3000, lambda: self.dealCommParaSerRead(1))
        elif sender is self.commParaNet1_ReadButton:  # 网口1 参数 读取
            self.dealCommParaNetRead()
        elif sender is self.commParaNet1_WrtieButton:  # 网口1 参数 写入
            self.dealCommParaNetWrite()
            QTimer.singleShot(2500, lambda: self.dealCommParaNetRead())

    def commParaGroupBoxRead(self):
        """通信参数读取"""
        self.dealCommParaSerRead(0)
        self.dealCommParaSerRead(1)
        self.dealCommParaNetRead()

    def remoteCtrlStatusDeal(self):
        """更新遥控出口使能状态"""
        if self.serRecvDir.get(0x0004) is None:
            return
        data = bytes2Int(self.serRecvDir.get(0x0004))
        if data & (1 << 4):
            self.remoteCtrlGroupBox.setTitle("遥控出口")
            self.remoteCtrlOffButton.setDisabled(False)
            self.remoteCtrlOnButton.setDisabled(False)
        else:
            self.remoteCtrlGroupBox.setTitle("遥控出口(无远方信号)")
            self.remoteCtrlOffButton.setDisabled(True)
            self.remoteCtrlOnButton.setDisabled(True)

    def createHomeDispParaGroupBox(self):
        self.HomeDispParaGroupBox = QGroupBox("显示设置")
        self.HomeDispParaWidgets = []
        layout = QVBoxLayout(self.HomeDispParaGroupBox)
        for home_disp_conf in mengy_spac800.HOME_DISP_ITEMS:
            if home_disp_conf.name == "一次/二次值选择":
                w = QComboBox()
                w.addItems(("二次值", "一次值"))
            else:
                w = QCheckBox(home_disp_conf.name)
            self.HomeDispParaWidgets.append((home_disp_conf, w))
            if home_disp_conf.enable:
                layout.addWidget(w)
        self.HomeDispPara_ReadButton = QPushButton("读取")
        self.HomeDispPara_WrtieButton = QPushButton("写入")
        self.HomeDispPara_ReadButton.clicked.connect(self.dealHomeDispParaClicked)
        self.HomeDispPara_WrtieButton.clicked.connect(self.dealHomeDispParaClicked)
        tmpLayout = QHBoxLayout()
        tmpLayout.addWidget(self.HomeDispPara_ReadButton)
        tmpLayout.addWidget(self.HomeDispPara_WrtieButton)
        layout.addLayout(tmpLayout)

    def dealHomeDispParaWrite(self):
        data32 = 0
        for home_disp_conf, w in self.HomeDispParaWidgets:
            if home_disp_conf.name == "一次/二次值选择":
                data32 += w.currentIndex() << home_disp_conf.offset
            else:
                if w.checkState():
                    data32 += 1 << home_disp_conf.offset
        data = (data32 % 65536, data32 // 65536)
        self.logger.info("write disp conf data is {:016b} {:016b}".format(*data))
        self.sendQueuePack(funCode=0x10, start=0x208, data=data)

    def dealHomeDispParaRead(self):
        if any((key not in self.serRecvDir.keys() for key in range(0x0208, 0x0208 + 2))):
            return
        data32 = bytes2Int(self.serRecvDir.get(0x0209) + self.serRecvDir.get(0x0208))
        data = (data32 % 65536, data32 // 65536)
        self.logger.info("read disp conf data is {:016b} {:016b}".format(*data))
        for home_disp_conf, w in self.HomeDispParaWidgets:
            if data32 & (1 << home_disp_conf.offset):
                if home_disp_conf.name == "一次/二次值选择":
                    w.setCurrentIndex(1)
                else:
                    w.setChecked(True)
            else:
                if home_disp_conf.name == "一次/二次值选择":
                    w.setCurrentIndex(0)
                else:
                    w.setChecked(False)

    def dealHomeDispParaClicked(self):
        sender = self.sender()
        if sender is self.HomeDispPara_ReadButton:
            self.dealHomeDispParaRead()
        else:
            self.dealHomeDispParaWrite()
        QTimer.singleShot(2500, lambda: self.dealHomeDispParaRead())

    def updateBySerialRecvBuff(self, changeAddrList):
        """数据缓存发生变化时需要刷新部分"""
        if 0x0004 in changeAddrList:
            self.remoteCtrlStatusDeal()  # 遥控跳闸使能控制
        if 0x0704 in changeAddrList:
            self.rewriteMeaEnableWgt()  # 测量板构件显示切换
        self.refreshTime = time.time()

    def rewriteMeaEnableWgt(self):
        """测量板使能影响的构件"""
        meaEnable = self.serRecvDir.get(0x0704)
        for w in (
            self.commParaSerAddr2,
            self.commParaSerBaud2,
            self.commParaSerPot2,
            self.meaGroupBox,
            self.energyGroupBox,
        ):
            if meaEnable == b"\x00\x01":  # 测量板投入
                w.setDisabled(False)
            else:
                w.setDisabled(True)

    def updateByTimer(self, timeout=1):
        if time.time() - self.refreshTime > timeout:
            self.remoteCtrlStatusDeal()  # 遥控跳闸使能控制
            self.rewriteMeaEnableWgt()
            self.refreshTime = time.time()

    def updateUnconditional(self):
        """无条件刷新界面显示部分"""
        pass

    def updateWhole(self):
        """更新全体显示构件"""
        self.datetimeParaGroupBoxRead()
        self.energeyGroupBoxRead()
        self.sysParaGroupBoxRead()
        self.commParaGroupBoxRead()
        self.dealHomeDispParaRead()
        self.rewriteMeaEnableWgt()
        self.refreshTime = time.time()


class Tab2_Sys_Para_BT(Tab2_Sys_Para):
    # 系统参数
    def createSysParaGroupBox(self):
        """创建系统参数显示构件"""
        self.sysParaOverRangeFlagPrt = False
        self.sysParaOverRangeFlagMea = False
        self.sysParaGroupBox = QGroupBox("系统参数")
        layout = QGridLayout(self.sysParaGroupBox)

        ptrGroupBox = QGroupBox("保护参数")
        ptrLayout = QGridLayout(ptrGroupBox)
        meaGroupBox = QGroupBox("测量参数")
        meaLayout = QGridLayout(meaGroupBox)

        self.sysParaPrtPT = QDoubleSpinBox()
        self.sysParaPrtPT.setDecimals(1)
        self.sysParaPrtPT.setSingleStep(0.1)
        addTooltipInSpinBox(self.sysParaPrtPT, (1.0, 1000.0))
        ptrLayout.addWidget(QLabel("母线PT变比"), 0, 0)
        ptrLayout.addWidget(self.sysParaPrtPT, 0, 1)

        self.sysParaPrtCT = QDoubleSpinBox()
        self.sysParaPrtCT.setDecimals(1)
        self.sysParaPrtCT.setSingleStep(0.1)
        addTooltipInSpinBox(self.sysParaPrtCT, (1.0, 1000.0))
        ptrLayout.addWidget(QLabel("进线PT变比"), 1, 0)
        ptrLayout.addWidget(self.sysParaPrtCT, 1, 1)

        self.sysParaPrtIX = QSpinBox()
        addTooltipInSpinBox(self.sysParaPrtIX, (1, 10000))
        ptrLayout.addWidget(QLabel("进线CT变比"), 2, 0)
        ptrLayout.addWidget(self.sysParaPrtIX, 2, 1)

        self.sysParaPrtI0 = QComboBox()
        self.sysParaPrtI0.addItems(mengy_spac800_bt.PRT_ONOFF_NAMES)
        ptrLayout.addWidget(QLabel("分位合位设置"), 3, 0)
        ptrLayout.addWidget(self.sysParaPrtI0, 3, 1)

        self.sysParaPrt_ReadButton = QPushButton("读取")
        self.sysParaPrt_WrtieButton = QPushButton("写入")
        ptrLayout.addWidget(self.sysParaPrt_ReadButton, 4, 0)
        ptrLayout.addWidget(self.sysParaPrt_WrtieButton, 4, 1)

        self.sysParaMeaPT = QDoubleSpinBox()
        self.sysParaMeaPT.setDecimals(1)
        self.sysParaMeaPT.setSingleStep(0.1)
        addTooltipInSpinBox(self.sysParaMeaPT, (0.1, 100.0))
        meaLayout.addWidget(QLabel("测量PT变比"), 1, 0)
        meaLayout.addWidget(self.sysParaMeaPT, 1, 1)

        self.sysParaMeaCT = QSpinBox()
        addTooltipInSpinBox(self.sysParaMeaCT, (1, 1000))
        meaLayout.addWidget(QLabel("测量CT变比"), 2, 0)
        meaLayout.addWidget(self.sysParaMeaCT, 2, 1)

        self.sysParaMeaCo = QComboBox()
        self.sysParaMeaCo.addItems(mengy_spac800.Mea_LN_NAMES)
        meaLayout.addWidget(QLabel("测量接线方式"), 3, 0)
        meaLayout.addWidget(self.sysParaMeaCo, 3, 1)

        self.sysParaMea_ReadButton = QPushButton("读取")
        self.sysParaMea_WrtieButton = QPushButton("写入")
        meaLayout.addWidget(self.sysParaMea_ReadButton, 4, 0)
        meaLayout.addWidget(self.sysParaMea_WrtieButton, 4, 1)

        self.sysParaPrt_ReadButton.clicked.connect(self.dealSysParaButtonClicked)
        self.sysParaPrt_WrtieButton.clicked.connect(self.dealSysParaButtonClicked)
        self.sysParaMea_ReadButton.clicked.connect(self.dealSysParaButtonClicked)
        self.sysParaMea_WrtieButton.clicked.connect(self.dealSysParaButtonClicked)

        meaGroupBox.setDisabled(True)
        layout.addWidget(ptrGroupBox, 0, 0)
        layout.addWidget(meaGroupBox, 1, 0)

    def sysParaGroupBoxReadByType(self, t):
        """系统参数读取"""
        if any((key not in self.serRecvDir.keys() for key in range(0x0200, 0x0207 + 1))):
            return
        overRangeList = []
        if t == 0:
            data = [bytes2Int(self.serRecvDir.get(0x0200 + i, b"\x00\x00")) for i in range(5)]
            self.logger.info("sys para prt is {}".format(data))
            self.sysParaPrtPT.setValue(data[0] * 0.1)
            maximum = self.sysParaPrtPT.maximum()
            minimum = self.sysParaPrtPT.minimum()
            if data[0] * 0.1 > maximum or data[0] * 0.1 < minimum:
                overRangeList.append("{} 读取值 {} 范围 {} ~ {}".format("母线PT变比定值", data[0] * 0.1, minimum, maximum))
                self.sysParaPrtPT.setStyleSheet("color:#ff0000;")
            else:
                self.sysParaPrtPT.setStyleSheet("")

            self.sysParaPrtCT.setValue(data[1] * 0.1)
            maximum = self.sysParaPrtCT.maximum()
            minimum = self.sysParaPrtCT.minimum()
            if data[1] * 0.1 > maximum or data[1] * 0.1 < minimum:
                overRangeList.append("{} 读取值 {} 范围 {} ~ {}".format("进线PT变比定值", data[1] * 1, minimum, maximum))
                self.sysParaPrtCT.setStyleSheet("color:#ff0000;")
            else:
                self.sysParaPrtCT.setStyleSheet("")

            self.sysParaPrtIX.setValue(data[2] * 1)
            maximum = self.sysParaPrtIX.maximum()
            minimum = self.sysParaPrtIX.minimum()
            if data[2] * 1 > maximum or data[2] * 1 < minimum:
                overRangeList.append("{} 读取值 {} 范围 {} ~ {}".format("进线CT变比定值", data[2] * 1, minimum, maximum))
                self.sysParaPrtIX.setStyleSheet("color:#ff0000;")
            else:
                self.sysParaPrtIX.setStyleSheet("")

            self.sysParaPrtI0.setCurrentIndex(data[3])
            if data[3] >= self.sysParaPrtI0.count():
                overRangeList.append("{} 读取值越限 {}".format("分位合位设置定值", data[3]))
                self.sysParaPrtI0.setStyleSheet("color:#ff0000;")
            else:
                self.sysParaPrtI0.setStyleSheet("")

            if len(overRangeList) > 0 and not self.sysParaOverRangeFlagPrt:
                self.sysParaOverRangeFlagPrt = True
                messageBox = ScrollMessageBox(msgList=overRangeList, width=430, parent=self)
                messageBox.setWindowTitle("系统参数-保护参数 超范围")
                messageBox.exec_()
                self.sysParaOverRangeFlagPrt = False
        else:
            data = [bytes2Int(self.serRecvDir.get(0x0205 + i, b"\x00\x00")) for i in range(3)]
            self.logger.info("sys para mea is {}".format(data))
            self.sysParaMeaPT.setValue(data[0] * 0.1)
            maximum = self.sysParaMeaPT.maximum()
            minimum = self.sysParaMeaPT.minimum()
            if data[0] * 0.1 > maximum or data[0] * 0.1 < minimum:
                # overRangeList.append("{} 读取值 {} 范围 {} ~ {}".format("测量CT变比定值", data[0] * 0.1, minimum, maximum))
                # self.sysParaMeaPT.setStyleSheet("color:#ff0000;")
                pass
            else:
                self.sysParaMeaPT.setStyleSheet("")

            self.sysParaMeaCT.setValue(data[1] * 1)
            maximum = self.sysParaMeaCT.maximum()
            minimum = self.sysParaMeaCT.minimum()
            if data[1] * 1 > maximum or data[1] * 1 < minimum:
                # overRangeList.append("{} 读取值 {} 范围 {} ~ {}".format("测量PT变比定值", data[1] * 1, minimum, maximum))
                # self.sysParaMeaCT.setStyleSheet("color:#ff0000;")
                pass
            else:
                self.sysParaMeaCT.setStyleSheet("")

            self.sysParaMeaCo.setCurrentIndex(data[2])
            if data[2] >= self.sysParaMeaCo.count():
                # overRangeList.append("{} 读取值越限 {}".format("测量接线方式选择定值", data[2]))
                # self.sysParaMeaCo.setStyleSheet("color:#ff0000;")
                pass
            else:
                self.sysParaMeaCo.setStyleSheet("")

            if len(overRangeList) > 0 and not self.sysParaOverRangeFlagMea:
                self.sysParaOverRangeFlagMea = True
                messageBox = ScrollMessageBox(msgList=overRangeList, width=330, parent=self)
                messageBox.setWindowTitle("系统参数-测量参数 超范围")
                messageBox.exec_()
                self.sysParaOverRangeFlagMea = False

    def dealSysParaButtonClicked(self):
        """系统参数按键处理"""
        sender = self.sender()
        # self.logger.info(sender is self.sysParaPrt_ReadButton)
        if sender is self.sysParaPrt_ReadButton:  # 读取保护变比
            self.sysParaGroupBoxReadByType(0)
        elif sender is self.sysParaPrt_WrtieButton:  # 写入保护变比
            data = []
            data.append(int(round(self.sysParaPrtPT.value() * (10 ** self.sysParaPrtPT.decimals()))))
            data.append(int(round(self.sysParaPrtCT.value() * (10 ** self.sysParaPrtCT.decimals()))))
            data.append(self.sysParaPrtIX.value())
            data.append(self.sysParaPrtI0.currentIndex())
            for w in (self.sysParaPrtPT, self.sysParaPrtCT, self.sysParaPrtIX, self.sysParaPrtI0):
                w.setStyleSheet("color:#ff0000;")
            self.logger.info("set sys ptr {}".format(data))
            self.sendQueuePack(funCode=0x10, start=0x0200, data=data)
            QTimer.singleShot(1500, lambda: self.sysParaGroupBoxReadByType(0))
        elif sender is self.sysParaMea_ReadButton:  # 读取测量变比
            self.sysParaGroupBoxReadByType(1)
        elif sender is self.sysParaMea_WrtieButton:  # 写入测量变比
            data = []
            data.append(int(round(self.sysParaMeaPT.value() * (10 ** self.sysParaMeaPT.decimals()))))
            data.append(self.sysParaMeaCT.value())
            data.append(self.sysParaMeaCo.currentIndex())
            for w in (self.sysParaMeaPT, self.sysParaMeaCT, self.sysParaMeaCo):
                w.setStyleSheet("color:#ff0000;")
            self.logger.info("set sys mea {}".format(data))
            self.sendQueuePack(funCode=0x10, start=0x0205, data=data)
            QTimer.singleShot(1500, lambda: self.sysParaGroupBoxReadByType(1))

    def createRemoteCtrlGroupBox(self):
        """遥控出口"""
        self.remoteCtrlGroupBox = QGroupBox("遥控出口")
        layout = QGridLayout(self.remoteCtrlGroupBox)
        self.remoteCtrlButtons = []

        button = QPushButton("遥控跳1DL")
        layout.addWidget(button, 0, 0)
        button.clicked.connect(self.dealRemoteCtrlButton)
        self.remoteCtrlButtons.append(button)
        button = QPushButton("遥控合1DL")
        layout.addWidget(button, 0, 1)
        button.clicked.connect(self.dealRemoteCtrlButton)
        self.remoteCtrlButtons.append(button)
        button = QPushButton("遥控跳2DL")
        layout.addWidget(button, 1, 0)
        button.clicked.connect(self.dealRemoteCtrlButton)
        self.remoteCtrlButtons.append(button)
        button = QPushButton("遥控合2DL")
        layout.addWidget(button, 1, 1)
        button.clicked.connect(self.dealRemoteCtrlButton)
        self.remoteCtrlButtons.append(button)
        button = QPushButton("遥控跳3DL")
        layout.addWidget(button, 2, 0)
        button.clicked.connect(self.dealRemoteCtrlButton)
        self.remoteCtrlButtons.append(button)
        button = QPushButton("遥控合3DL")
        layout.addWidget(button, 2, 1)
        button.clicked.connect(self.dealRemoteCtrlButton)
        self.remoteCtrlButtons.append(button)

    def dealRemoteCtrlButton(self):
        """遥控分合闸按键处理"""
        sender = self.sender()
        if sender in self.remoteCtrlButtons:
            choice = QMessageBox.question(self, "警告", "执行->{}？".format(sender.text()), QMessageBox.Yes | QMessageBox.No)
            if choice == QMessageBox.Yes:
                idx = self.remoteCtrlButtons.index(sender)
                self.sendQueuePack(funCode=0x06, start=0x0B00 + idx, value=0xFF00)

    def remoteCtrlStatusDeal(self):
        """更新遥控出口使能状态"""
        if self.serRecvDir.get(0x0004) is None:
            return
        data = bytes2Int(self.serRecvDir.get(0x0004))
        if data & (1 << 4):
            self.remoteCtrlGroupBox.setTitle("遥控出口")
            for button in self.remoteCtrlButtons:
                button.setDisabled(False)
        else:
            self.remoteCtrlGroupBox.setTitle("遥控出口(无远方信号)")
            for button in self.remoteCtrlButtons:
                button.setDisabled(True)

    def createEnergyGroupBox(self):
        super().createEnergyGroupBox()
        self.energyGroupBox.setDisabled(True)

    def rewriteMeaEnableWgt(self):
        """测量板使能影响的构件"""
        meaEnable = self.serRecvDir.get(0x0704)
        for w in (
            self.commParaSerAddr2,
            self.commParaSerBaud2,
            self.commParaSerPot2,
            self.energyGroupBox,
        ):
            if meaEnable == b"\x00\x01":  # 测量板投入
                w.setDisabled(False)
            else:
                w.setDisabled(True)

    def createHomeDispParaGroupBox(self):
        self.HomeDispParaGroupBox = QGroupBox("显示设置")
        self.HomeDispParaWidgets = []
        layout = QVBoxLayout(self.HomeDispParaGroupBox)
        for home_disp_conf in mengy_spac800_bt.HOME_DISP_ITEMS:
            if home_disp_conf.name == "一次/二次值选择":
                w = QComboBox()
                w.addItems(("二次值", "一次值"))
            else:
                w = QCheckBox(home_disp_conf.name)
            self.HomeDispParaWidgets.append((home_disp_conf, w))
            if home_disp_conf.enable:
                layout.addWidget(w)
        self.HomeDispPara_ReadButton = QPushButton("读取")
        self.HomeDispPara_WrtieButton = QPushButton("写入")
        self.HomeDispPara_ReadButton.clicked.connect(self.dealHomeDispParaClicked)
        self.HomeDispPara_WrtieButton.clicked.connect(self.dealHomeDispParaClicked)
        tmpLayout = QHBoxLayout()
        tmpLayout.addWidget(self.HomeDispPara_ReadButton)
        tmpLayout.addWidget(self.HomeDispPara_WrtieButton)
        layout.addLayout(tmpLayout)
